package com.gframework.core.service.observed;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.aspectj.lang.reflect.MethodSignature;

/**
 * 被观察者标识注解.
 * 
 * <p>这是一个多对多的利用AOP+IOC方式实现的观察者模型逻辑。
 * 将此注解配置到bean的一个方法上，那么只要他被执行了，就会通知所有指定的观察者执行特定的方法。
 * 
 * <p><strong>【观察者模式注意事项】</strong><br>
 * 你不得不在考虑一个循环调用问题。如果两个甚至多个bean形成了循环观察的情况，那么就会发生一个错误。
 * 你必须保证你的业务逻辑代码中不会存在这样的问题。
 * 
 * <p>实现观察者操作可以有两种方式：
 * <pre>
 * 1、利用此注解方式：
 * 使用此注解可以较为简单的实现观察者逻辑，观察者只需要实现特定接口复写方法即可，
 * 而被观察者只需要使用注解注入到bean即可。同时为了防止循环调用问题，AOP中有一个
 * TTL的概念，可以防止因为循环调用无法退出导致的死循环问题。
 * <strong>但是需要注意，这并不能够避免循环调用问题，只是能够避免循环调用导致的死循环。</strong>
 * 
 * 2、自己通知观察者方式：
 * 如果你需要实现更加复杂的逻辑，例如只有在方法中特定逻辑被触发后，才通知观察者，那么可以自己在bean中注入
 * 接口的所有子类，然后循环调用即可。但是需要注意的是，这种方式你必须自己处理好循环调用问题。
 * </pre>
 * 
 * <p>观察者可以通过 {@link #value()}进行配置，这个配置的是一个类或一个接口，但必须是spring的bean。
 * 一旦此方法被执行，会通过Spring的ApplicationContext找到所有指定类型或子类bean，并执行特定的方法。
 * 
 * <p>观察者的方法参数必须符合以下形式之一，同时方法必须是public的：
 * <ul>
 * <li>方法没有任何参数</li>
 * <li>方法只有一个{@link MethodSignature}类型参数</li>
 * <li>方法具有和被观察者同样的参数以及顺序，参数结尾可以有一个可选的 {@link MethodSignature}参数</li>
 * </ul>
 * <p>MethodSignature类型是被观察的方法签名对象，可以通过他获取到方法相关信息。
 * <p>如果value指定的类型是一个函数式接口，那么无需指定method，会执行默认方法。
 * 否则会去寻找同名方法，或者你可以通过{@link #method()}去指定要执行的方法名称。
 * 
 * <p>需要注意的是，配置错错误（如方法参数不正确，方法不存在等）只有在运行期间才能够被发现。
 * 
 * @since 1.0.0
 * @author Ghwolf
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface ObservedMethod {
	/**
	 * 指定观察者类型.
	 * <p>可以是类或接口，如果是函数式接口，那么会直接调用默认方法。
	 * 如果是其他类型，则会调用同名的方法。或者你可以通过{@link #method()}指定调用的方法。
	 * <p>观察者的方法参数必须符合以下形式之一，同时方法必须是public的：
	 * <li>方法没有任何参数</li>
	 * <li>方法只有一个{@link MethodSignature}类型参数</li>
	 * <li>方法具有和被观察者同样的参数以及顺序，参数结尾可以有一个可选的 {@link MethodSignature}参数</li>
	 */
	Class<?> value() ;
	/**
	 * 观察者执行的方法名称.
	 * <p>如果指定了则会执行执行的方法，如果没有指定，如果类型是函数是接口，则会调用默认方法，否则会寻找同名方法。
	 * <p>观察者的方法参数必须符合以下形式之一，同时方法必须是public的：
	 * <li>方法没有任何参数</li>
	 * <li>方法只有一个{@link MethodSignature}类型参数</li>
	 * <li>方法具有和被观察者同样的参数以及顺序，参数结尾可以有一个可选的 {@link MethodSignature}参数</li>
	 */
	String method() default "";

}
